package cn.enited.devtools.apiaggregator.exception;

import cn.enited.devtools.apiaggregator.consts.ErrorCode;
import cn.hutool.core.util.StrUtil;
import cn.enited.devtools.apiaggregator.dto.ActionResult;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;
import java.util.List;
import java.util.stream.Collectors;

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(value = BizException.class)
    public ActionResult handleBizException(BizException e) {
        return ActionResult.of(e.getErrorCode(), e.getMessage());
    }

    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public ActionResult handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        BindingResult bindResult = e.getBindingResult();
        List<String> errors = bindResult.getAllErrors().stream().map(ObjectError::getDefaultMessage).collect(Collectors.toList());
        return getErrorMessage(errors);
    }

    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(value = ValidationException.class)
    public ActionResult handleValidationException(ValidationException e) {
        if (e instanceof ConstraintViolationException) {
            ConstraintViolationException ex = (ConstraintViolationException) e;
            List<String> errors = ex.getConstraintViolations().stream().map(ConstraintViolation::getMessage).collect(Collectors.toList());
            return getErrorMessage(errors);
        }
        return ActionResult.of(ErrorCode.WRONG_PARAM);
    }

    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(BindException.class)
    public ActionResult handleBindException(BindException e) {
        List<String> errors = e.getAllErrors().stream().map(ObjectError::getDefaultMessage).collect(Collectors.toList());
        return getErrorMessage(errors);
    }

    private ActionResult getErrorMessage(List<String> errors) {
        String message = String.join("\n", errors);
        return ActionResult.of(ErrorCode.WRONG_PARAM.code(), message);
    }

    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(ServletRequestBindingException.class)
    public ActionResult handleServletRequestBindingException(ServletRequestBindingException e) {
        if (e instanceof MissingServletRequestParameterException) {
            MissingServletRequestParameterException me = (MissingServletRequestParameterException) e;
            String message = StrUtil.format("缺少参数 {}", me.getParameterName());
            return ActionResult.of(ErrorCode.WRONG_PARAM.code(), message);
        } else if (e instanceof MissingPathVariableException) {
            MissingPathVariableException me = (MissingPathVariableException) e;
            String message = StrUtil.format("缺少参数 {}", me.getVariableName());
            return ActionResult.of(ErrorCode.MESSING_PARAM.code(), message);
        }
        return ActionResult.of(ErrorCode.WRONG_PARAM);
    }

    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(value = Exception.class)
    public ActionResult handleException(Exception e) {
        return ActionResult.of(ErrorCode.ERROR);
    }
}
